//
//  Client.cpp
//  Projet A2014
//
//  Created by Jules Thuillier on 03/11/2014.
//  Copyright (c) 2014 Jules Thuillier Erwan Marchand. All rights reserved.
//

#include "Client.h"
#include "InputClient.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <string>
#include <stdio.h>     
#include <stdlib.h>     
#include <time.h> 

#define _MAX_HOST_LENGTH_ 256

using namespace std;

Client::Client(){
	srand(time(NULL));
	TexteRecu = "";
	nombreCandidats = 0;
	TraitementFait = 0;
    
    InputClient *myInput = new InputClient();
    clientIP = myInput->getIP();
    Port = myInput->getPort();
	
	bool everythingOK = false;
	everythingOK = initLibWinsock();
	if (everythingOK)
		everythingOK = initSocket();
	if (everythingOK)
		everythingOK = scannerAdresses();
	if (everythingOK)
		connectionAuServeur();
	if (everythingOK)
		receive();
   
}


/// Initialisation de la librairie Winsock
bool Client::initLibWinsock()
{
    // Initialize the winsock library
    
    int iResult;
    
    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed: %d\n", iResult);
        return false;
    }
    return true;
}

/// Initialisation du socket
bool Client::initSocket()
{
    leSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (leSocket == INVALID_SOCKET) {
        printf("Erreur de socket(): %ld\n\n", WSAGetLastError());
        freeaddrinfo(result);
        WSACleanup();
        return false;
    }
    return true;

}

/// Scanner les adresses IP client serveur
bool Client::scannerAdresses()
{
    // 1 - On va chercher l'adresse du serveur en utilisant la fonction getaddrinfo.
    ZeroMemory( &hints, sizeof(hints) );
    hints.ai_family = AF_INET;        // Famille d'adresses
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;  // Protocole utilisÈ par le serveur
    
    // 2 - getaddrinfo obtient l'adresse IP du host donnÈ
    int iResult;
    iResult = getaddrinfo(clientIP, Port, &hints, &result);
    if ( iResult != 0 ) {
        printf("Erreur de getaddrinfo: %d\n\n", iResult);
        WSACleanup();
        return false;
    }
    //---------------------------------------------------------------------
    // 3 - On parcours les adresses retournees jusqu'a trouver la premiere adresse IPV4
    while((result != NULL) &&(result->ai_family!=AF_INET))
        result = result->ai_next;


    if (((result == NULL) ||(result->ai_family!=AF_INET))) {
        freeaddrinfo(result);
        printf("Impossible de recuperer la bonne adresse\n");
        WSACleanup();
        return false;
    }

    adresse=(struct sockaddr_in *) result->ai_addr;
	std::cout << "Adresse trouvee pour le serveur : " << inet_ntoa(adresse->sin_addr) << std::endl;

	return true;
}



/// Connection au serveur
bool Client::connectionAuServeur()
{
    printf("Tentative de connexion au serveur\n");
    // On va se connecter au serveur en utilisant l'adresse qui se trouve dans
    // la variable result.
    int iResult = connect(leSocket, result->ai_addr, (int)(result->ai_addrlen));
    if (iResult == SOCKET_ERROR) {
        printf("Impossible de se connecter au serveur %s sur le port %s\n", inet_ntoa(adresse->sin_addr),Port);
        freeaddrinfo(result);
        WSACleanup();
        return false;
    }
    
    printf("Connecte au serveur %s:%s\n\n", clientIP, Port);
    freeaddrinfo(result);
    return true;
}

/// Ajoute les derniers caracteres recus a l attribut TexteRecu
void Client::ajouterMot(char* motRecu)
{
	TexteRecu += motRecu;
}

/// Verifie si le reception est finie (on a recu le "END")
bool Client::ReceptionFinie()
{
	bool resultat = false;
	int longueur = TexteRecu.length();
	if (longueur >= 3 && TexteRecu[longueur - 5] == 'E' && TexteRecu[longueur - 4] == 'N' && TexteRecu[longueur - 3] == 'D')
		resultat = true;
	return resultat;
}

/// Traite les donnees recues et transferees dans l attribut TextRecu afin d ajouter le nombre de candidats et les noms de ceux-ci aux attributs
void Client::traiterDonneesRecu()
{
	int longueur = TexteRecu.length() - 2;
	int i = 0;
	// tant que nous ne sommes pas arrive a la fin
	while (!(TexteRecu[i] == 'E' && TexteRecu[i + 1] == 'N' && TexteRecu[i + 2] == 'D'))
	{
		// cas ou on detecte "NBC" ( suivit du nombre de candidats )
		if (TexteRecu[i] == 'N' && TexteRecu[i + 1] == 'B' && TexteRecu[i + 2] == 'C')
		{
			int j = i + 4;
			int longueurNombre = 0;
			// on verifie la longueur de ce nombre
			while (!(TexteRecu[j] == 'E' && TexteRecu[j + 1] == 'N' && TexteRecu[j + 2] == 'D') && !(TexteRecu[j] == 'N' && TexteRecu[j + 1] == 'B' && TexteRecu[j + 2] == 'C') && !(TexteRecu[j] == 'C' && TexteRecu[j + 1] == 'A' && TexteRecu[j + 2] == 'N'))
			{
				longueurNombre++;
				j++;
			}
			longueurNombre -= 2;
			i += 4;
			std::string nombreString = "";
			// on recupere le nombre
			for (int k = i; k < i + longueurNombre; k++)
				nombreString += TexteRecu[k];
			// on le convertis en int et on le sauvegarde dans les attributs
			nombreCandidats = atoi(nombreString.c_str());
		}

		// cas ou on detecte "CAN" ( suivit du nom d un candidat )
		else if (TexteRecu[i] == 'C' && TexteRecu[i + 1] == 'A' && TexteRecu[i + 2] == 'N')
		{
			int j = i + 4;
			int longueurNom = 0;
			// on verifie la longueur de ce nom
			while (!(TexteRecu[j] == 'E' && TexteRecu[j + 1] == 'N' && TexteRecu[j + 2] == 'D') && !(TexteRecu[j] == 'N' && TexteRecu[j + 1] == 'B' && TexteRecu[j + 2] == 'C') && !(TexteRecu[j] == 'C' && TexteRecu[j + 1] == 'A' && TexteRecu[j + 2] == 'N'))
			{
				longueurNom++;
				j++;
			}
			longueurNom -= 2;
			i += 4;
			std::string nom = "";
			// on recupere le nom
			for (int k = i; k < i + longueurNom; k++)
				nom += TexteRecu[k];
			// on le sauvegarde dans les attributs
			listeCandidats.push_back(nom);

		}
		i++;


	}
}

/// Permet de choisir au hasard le candidat pour lequel voter
std::string Client::choisirRandom()
{
	int random = rand() % nombreCandidats;
	std::cout << " ==> Le client a vote pour : " << listeCandidats[random] << std::endl << std::endl;
	return listeCandidats[random];
}


/// Fermeture du socket
void Client::fermerSocket()
{
    closesocket(leSocket);
    WSACleanup();
}

// Communique avec le serveur
void Client::receive()
{
	

	while (1)
	{
		while (!ReceptionFinie())
		{
			int recvBytes = 0;
			char motRecu[_MAX_HOST_LENGTH_];
			// Maintenant, on va recevoir l' information envoyée par le serveur
			int iResult = recv(leSocket, motRecu, _MAX_HOST_LENGTH_ - 1, 0);
			if (iResult > 0) {
				//printf("Nombre d'octets recus: %d\n", iResult);
				motRecu[iResult] = '\0';
				//printf("Le mot recu est %*s\n", iResult, motRecu);
				ajouterMot(motRecu);

			}

			else
			{
				printf("Erreur de reception : %d\n\n", WSAGetLastError());
				return;
			}
		}
		if (TraitementFait == 0)
			{
				
				traiterDonneesRecu();
				// Envoi du message
				string message = "CHOIX:"+choisirRandom()+"\r\n";
				int nBytes = send(leSocket, message.c_str(), strlen(message.c_str()), 0);
				if (SOCKET_ERROR == nBytes)
				{
					printf("\nErreur durant l'envoi sur le socket");
					// FERMER LE SOCKET ET QUITTER LA BOUCLE
					
				}
				TraitementFait = 1;
				fermerSocket();
				break;

			}
		}
}

